package com.theBlind.orm;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

/**
 *
 *
 * 1.解析xml配置文件
 * 2. 参数映射 --》sql解析（暂不支持）---》sql执行---》结果映射
 * 3.mapper接口层代理
 *
 * @program: orm
 * @description:
 * @author: TheBlind
 * @create: 2021-04-22 15:27
 **/
public class SqlSessionFactoryBuilder {


    public SqlSessionFactory build(Reader reader) {
        SAXReader saxReader = new SAXReader();
        try {
            Document configurationXml = saxReader.read(new InputSource(reader));
            Configuration configuration = parseConfiguration(configurationXml.getRootElement());
            return new DefaultSqlSessionFactory(configuration);

        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Configuration parseConfiguration(Element root) {
        Configuration configuration = new Configuration();

        configuration.setDataSource(dataSource(root.selectNodes("//dataSource")));
        configuration.setMapperElement(mapperElement(root.selectNodes("mappers")));
        return configuration;
    }


    private DataSourceProperty dataSource(List<Element> list) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        Element element = list.get(0);
        List content = element.content();
        for (Object o : content) {
            Element e = (Element) o;
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            switch (name) {
                case "driver":
                    dataSourceProperty.setDriver(value);
                    break;
                case "url":
                    dataSourceProperty.setUrl(value);
                    break;
                case "username":
                    dataSourceProperty.setUsername(value);
                    break;
                case "password":
                    dataSourceProperty.setPassword(value);
                    break;
            }
        }
        return dataSourceProperty;
    }

    private Connection connection(Map<String, String> dataSource) {
        try {
            Class.forName(dataSource.get("driver"));
            return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * <mappers>
     * <mapper resource="mapper/User_Mapper.xml"/>
     * </mappers>
     *
     * @param list
     * @return
     */
    private Map<String, MapperStatement> mapperElement(List<Element> list) {
        Map<String, MapperStatement> mappers = new HashMap<>();

        Element element = list.get(0);
        //处理存放 sql的xml文件
        element.content().stream().forEach(mapperXml -> {

            Element elementXmlObj = (Element) mapperXml;
            String resourcePath = elementXmlObj.attributeValue("resource");

            try {
                Reader resourceAsReader = getResourceAsReader(resourcePath);
                SAXReader saxReader = new SAXReader();
                Document mapperDocument = saxReader.read(new InputSource(resourceAsReader));
                Element rootElement = mapperDocument.getRootElement();
                String namespace = rootElement.attributeValue("namespace");

                List<Element> selectNodes = rootElement.selectNodes("select");

                /**
                 *   <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.bugstack.middleware.mybatis.test.po.User">
                 *         SELECT id, userId, userNickName, userHead, userPassword, createTime
                 *         FROM user
                 *         where id = #{id}
                 *     </select>
                 */
                selectNodes.forEach((node) -> {
                    String id = node.attributeValue("id");
                    String parameterType = node.attributeValue("parameterType");
                    String resultType = node.attributeValue("resultType");
                    String sql = node.getText();

                    Map<String, Integer> parameter = new HashMap<>();
                    //处理 #{}
                    Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                    Matcher matcher = pattern.matcher(sql);
                    for (int i = 0; matcher.find(); i++) {
                        //group 针对的是()
                        String g1 = matcher.group(1);
                        String g2 = matcher.group(2);
                        parameter.put(g2,i );
                        sql = sql.replace(g1, "?");
                    }

                    MapperStatement mapperStatement = new MapperStatement();
                    mapperStatement.setNamespace(namespace);
                    mapperStatement.setId(id);
                    mapperStatement.setParameterType(parameterType);
                    mapperStatement.setResultType(resultType);
                    mapperStatement.setSql(sql);
                    mapperStatement.setParameter(parameter);

                    mappers.put(namespace + "." + id, mapperStatement);

                });
            } catch (IOException | DocumentException e) {
                e.printStackTrace();
            }
        });
        return mappers;
    }

    public static Reader getResourceAsReader(String resource) throws IOException {

        // path 不以’/'开头时默认是从此类所在的包下取资源，以’/'开头则是从
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        InputStream resourceAsStream = classLoader.getResourceAsStream(resource);


        return new InputStreamReader(resourceAsStream);
    }


}
